/**************************************************************************************************
 **Company       : BURNON TECHNOLOGY LTD.
 **Project Name	 : dsPIC33CK64MP206 BOOT
 **File:         : Framework.c
 **Created by  	 : YangWQ
 **Created Date	 : June 18, 2019, 1:19 PM
 **Version:      : v0.1
 **Target  		 : dsPIC33CK64MP206
 **HardWare		 : 
 **SofeWare      : MPLAB X IDE v5.20 + XC16 v1.36 
 **************************************************************************************************/
#include  <string.h>
#include  <stdio.h>
#include "../src/Framework.h"
#include "../src/SPI_FrameWork.h"
#include "../src/SPI.h"
#include "../src/PanelComm.h"
#include "../src/CommandHandler.h"
#include "../src/Debug.h"
#include "config/pic32mk_mcm_curiosity_pro/peripheral/nvm/plib_nvm.h"
#include "Uart5.h"
#include "config/pic32mk_mcm_curiosity_pro/peripheral/gpio/plib_gpio.h"
#include "../src/config/pic32mk_mcm_curiosity_pro/peripheral/spi/spi_master/plib_spi4_master.h"
#include "Common.h"

static BOOL  RunApplication = FALSE;      
static BOOL  RxFrameValid = FALSE;          

BOOL  Erase_Flash_Enable = FALSE;
BOOL  ProgramFlash_Enable = FALSE;

uint8_t   RuningMode = 0x01;//BOOT_MODE;
uint16_t  CRC_Data=0;               //16-bit global variable: CRC check value

uint8_t slave_num = 1;    //007 added, slave number, default slave 0

BootMode CurMode = NORMAL_MODE;

BootMode GetMode(void)
{
    return CurMode;
}

void SetModeSlave(BootMode mode, uint8_t slaveNum)
{
    CurMode = mode;
    slave_num = slaveNum;

    if (mode == PROGRAM_MODE && slaveNum == 1) 
    {
        Erase_Flash_Enable = TRUE;
    } 
    else
    {
        Erase_Flash_Enable = FALSE;
    }
    ProgramFlash_Enable = FALSE;
}

uint8_t GetSlaveNum(void)
{
    return slave_num;
}

BOOL IsEraseFlashEnabled(void)
{
    return Erase_Flash_Enable;
}


BOOL IsProgramFlashEnabled(void)
{
    return ProgramFlash_Enable;
}

void SetProgramFlashEnabled(BOOL enabled)
{
    ProgramFlash_Enable = enabled;
}

void SetEraseFlashEnabled(BOOL enabled)
{
    Erase_Flash_Enable = enabled;
}

uint16_t GetCrcData(void)
{
    return CRC_Data;
}
/********************************************************************
* Function: 	FrameWorkTask()
* Precondition: 
* Input: 		None.
* Output:		None.
* Side Effects:	None.
* Overview: 	Process the command if there is a valid fame.			
* Note:		 	None.
********************************************************************/
void FrameWorkTask(void)
{
	if(RxFrameValid)  
	{
		// Valid frame received, process the command.

		// Reset the flag.
		RxFrameValid = FALSE;	
	}        
}


void  UART_Response_Func(void)  
{
    // uint8_t Uart_TxCnt;  
    uint8_t SPI_TxCnt;   
    uint8_t cmd_temp;    
    
    if(SPI_WorkMsg.Read_En)          
    {
        if(SPI_WorkMsg.DlayValue==0)   
        {
            SPI_WorkMsg.Read_En = 0;  
            SPI_TxCnt = 0;
            // Uart_TxCnt = 1; 
            cmd_temp = SPI_WorkMsg.Cmd;  
            switch(cmd_temp)
            {
                case READ_VER_INFO_SPI:     //0xC1
                case SET_MODE_SPI:             //0x42 
                    return;
                case ERASE_FLASH_SPI:   //0x43
                    New_SPI_MSG.WData[SPI_TxCnt++] = SPI_WorkMsg.Cmd | CMD_SPI_READ_FLAY;   
                    New_SPI_MSG.WData[SPI_TxCnt++] = 1;       // len
                    New_SPI_MSG.WData[SPI_TxCnt++] = 0x00;    // state
                    New_SPI_MSG.Len = SPI_TxCnt;              // Cmd + len + state 
                    if(SPI_ReadInstruction(GetSlaveNum(), &New_SPI_MSG)==0)  
                    {
                        PanelCommRplyPacket.Data[0] = New_SPI_MSG.RData[0];
                    }
                    else      
                    {
                        PanelCommRplyPacket.Data[0] = R_INVALID;
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = ERASE_FLASH_BACK; //0xF6
                    
                    break;
                case PROGRAM_FLASH_SPI:  //0x44
                    New_SPI_MSG.WData[SPI_TxCnt++] = SPI_WorkMsg.Cmd | CMD_SPI_READ_FLAY;   
                    New_SPI_MSG.WData[SPI_TxCnt++] = 1;       // len
                    New_SPI_MSG.WData[SPI_TxCnt++] = 0x00;    // state
                    New_SPI_MSG.Len = SPI_TxCnt;              // Cmd + len + state 
                    if(SPI_ReadInstruction(GetSlaveNum(), &New_SPI_MSG)==0)  
                    {
                        PanelCommRplyPacket.Data[0] = New_SPI_MSG.RData[0]; 
                    }
                    else
                    {
                        PanelCommRplyPacket.Data[0] = R_INVALID;
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = PROGRAM_FLASH_BACK;   //0xF7

                    break;
                case CHECK_CRC_SPI:  //0x45
                    New_SPI_MSG.WData[SPI_TxCnt++] = SPI_WorkMsg.Cmd | CMD_SPI_READ_FLAY;  
                    New_SPI_MSG.WData[SPI_TxCnt++] = 1;       // len
                    New_SPI_MSG.WData[SPI_TxCnt++] = 0x00;    // state
                    New_SPI_MSG.Len = SPI_TxCnt;            // Cmd + len + state 
                    if(SPI_ReadInstruction(GetSlaveNum(), &New_SPI_MSG)==0)  
                    {
                        PanelCommRplyPacket.Data[0] = New_SPI_MSG.RData[0]; 
                    }
                    else
                    {
                        PanelCommRplyPacket.Data[0] = R_INVALID;
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = CHECK_CRC_BACK;   //0xF8
                    
                    break;
            
                default:return;
            } 

            PopulateRplyPack();
            PanelComm_FillTxBytesWithRplyPack();
        }   
    }   
}

bool TxVaild = false;
bool ReSend_ck = false;
uint16_t ReSend_ck_DlayValue = 0;
extern uint8_t SPI2_Trans_Tx[12];
static uint8_t PanelRxData_CK_buf[PANEL_BUF_LEN];
void RotateAxis_Response_Func(void)  
{
    uint8_t cmd_temp;    
    uint16_t calCrc;

    if(SPI_WorkMsg.Read_En_ck)          
    {
        if(SPI_WorkMsg.DlayValue==0)   
        {
            TxVaild = true;
            SPI_WorkMsg.Read_En_ck = 0;
            cmd_temp = SPI_WorkMsg.Cmd;  
            switch(cmd_temp)
            {
                case READ_VER_INFO_SPI:     //0xC1
                case SET_MODE_SPI:             //0x42 
                    return;
                case ERASE_FLASH_SPI:   //0x43             
                    SlaveSelect(7);
                    delay(50);
                    SPI4_WriteRead(SPI2_Trans_Tx,12, PanelRxData_CK_buf, 12); //read
                    delay(100);
                    memcpy(&PanelRxData_CK[0], &PanelRxData_CK_buf, 12);
                    
                    calCrc = CRC16_Chk_Rx_CK(1,8);
                    PanelCommRecPacket_CK.Crc = PanelRxData_CK[9] | (PanelRxData_CK[10] << 8);
                    if (calCrc != PanelCommRecPacket_CK.Crc) 
                    {
                        PanelCommRplyPacket.Data[0] = 1;
                    }
                    else
                    {
                        PanelCommRplyPacket.Data[0] = PanelRxData_CK[6];
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = ERASE_FLASH_BACK; //0xF6
                        
                    break;
                case PROGRAM_FLASH_SPI:  //0x44                 
                    SlaveSelect(7);
                    delay(50);
                    SPI4_WriteRead(SPI2_Trans_Tx,12, PanelRxData_CK, 12); //read
                    delay(100);
                   
                    calCrc = CRC16_Chk_Rx_CK(1,8);
                    PanelCommRecPacket_CK.Crc = PanelRxData_CK[9] | ( PanelRxData_CK[10] << 8 );
                    if (calCrc != PanelCommRecPacket_CK.Crc) 
                    {
                        PanelCommRplyPacket.Data[0] = 1;
                    }
                    else
                    {
                        PanelCommRplyPacket.Data[0] = PanelRxData_CK[6];
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = PROGRAM_FLASH_BACK;   //0xF7
                    break;
                case CHECK_CRC_SPI:  //0x45
                    SlaveSelect(7);
                    delay(10);
                    SPI4_WriteRead(SPI2_Trans_Tx,12, PanelRxData_CK, 12); //read
                    delay(10);
                    
                    calCrc = CRC16_Chk_Rx_CK(1,8);
                    PanelCommRecPacket_CK.Crc = PanelRxData_CK[9] | ( PanelRxData_CK[10] << 8 );
                    if (calCrc != PanelCommRecPacket_CK.Crc) 
                    {
                        PanelCommRplyPacket.Data[0] = 1;
                    }
                    else
                    {
                        PanelCommRplyPacket.Data[0] = PanelRxData_CK[6];
                    }
                    PanelCommRplyPacket.DataByteLength = 1;
                    PanelCommRplyPacket.Cmd = CHECK_CRC_BACK;   //0xF8
                    break;
                default:return;
            } 
            PopulateRplyPack();
            PanelComm_FillTxBytesWithRplyPack();
        }
    }
}


/********************************************************************
* Function: 	BuildRxFrame()
* Precondition: 
* Input: 		Pointer to Rx Data and Rx byte length.
* Output:		None.
* Side Effects:	None.
* Overview: 	Builds rx frame and checks CRC.			
* Note:		 	None.
********************************************************************/
void BuildRxFrame(uint8_t *RxData, INT16 RxLen)  
{
	// static BOOL Escape = FALSE; 
	// WORD_VAL crc;   
	// while((RxLen > 0) && (!RxFrameValid)) 
	// {
	// 	RxLen--; 
	// 	if(UartRxBuff.Len >= sizeof(UartRxBuff.Buf)) 
	// 	{
	// 		UartRxBuff.Len = 0;
	// 	}			
	// 	switch(*RxData)  
	// 	{
	// 		case SOH: //Start of header  
	// 			if(Escape) 
	// 			{
	// 				// Received byte is not SOH, but data.
	// 				UartRxBuff.Buf[UartRxBuff.Len++] = *RxData;
	// 				// Reset Escape Flag.
	// 				Escape = FALSE; 
	// 			}
	// 			else
	// 			{
	// 				// Received byte is indeed a SOH which indicates start of new frame.
	// 				UartRxBuff.Len = 0; 
	// 			}		
	// 			break;	
	// 		case EOT: // End of transmission 04
	// 			if(Escape)
	// 			{
	// 				// Received byte is not EOT, but data.
	// 				UartRxBuff.Buf[UartRxBuff.Len++] = *RxData; 
	// 				// Reset Escape Flag.
	// 				Escape = FALSE; 
	// 			}
	// 			else    
	// 			{
	// 				// Received byte is indeed a EOT which indicates end of frame.
	// 				// Calculate CRC to check the validity of the frame.
	// 				if(UartRxBuff.Len > 1)  
	// 				{
	// 					crc.byte.LB = UartRxBuff.Buf[UartRxBuff.Len-2]; 
	// 					crc.byte.HB = UartRxBuff.Buf[UartRxBuff.Len-1]; 

    //                     if((Crc_Fun(UartRxBuff.Buf, (uint32_t)(UartRxBuff.Len-2), 0) == crc.Val) && (UartRxBuff.Len > 2))   
	// 					{
	// 						// CRC matches and frame received is valid.
	// 						RxFrameValid = TRUE;  							
	// 					}
	// 				}					
	// 			}							
	// 			break;					
	// 	    case DLE: // Escape character received.
	// 			if(Escape)
	// 			{
	// 				// Received byte is not ESC but data.
	// 				UartRxBuff.Buf[UartRxBuff.Len++] = *RxData;
	// 				// Reset Escape Flag.
	// 				Escape = FALSE;					
	// 			}
	// 			else
	// 			{
	// 				// Received byte is an escape character. Set Escape flag to escape next byte.
	// 				Escape = TRUE;					
	// 			}	
	// 			break;
	// 		default: // Data field.
	// 		    UartRxBuff.Buf[UartRxBuff.Len++] = *RxData;
	// 		    // Reset Escape Flag.
	// 		    Escape = FALSE;
	// 			break;			
	// 	}
	// 	//Increment the pointer.
	// 	RxData++;	
	// }	
}	


/********************************************************************
* Function: 	GetTransmitFrame()
* Precondition: 
* Input: 		Buffer pointer.
* Output:		Length of the buffer.
* Side Effects:	None.
* Overview: 	Gets the complete transmit frame into the "Buff".			
* Note:		 	None.
********************************************************************/
UINT GetTransmitFrame(uint8_t* Buff)   
{
	// INT BuffLen = 0;   
	// WORD_VAL crc;  
	// uint8_t i;
	// if(UartTxBuff.Len)  
	// {
	// 	//There is something to transmit.
	// 	// Calculate CRC of the frame.
    //     crc.Val = Crc_Fun(UartTxBuff.Buf, (uint32_t)UartTxBuff.Len, 0);   
	// 	UartTxBuff.Buf[UartTxBuff.Len++] = crc.byte.LB;  
	// 	UartTxBuff.Buf[UartTxBuff.Len++] = crc.byte.HB;   	
		
	// 	// Insert SOH (Indicates beginning of the frame)	
	// 	Buff[BuffLen++] = SOH;  
		
	// 	// Insert Data Link Escape Character.
	// 	for(i = 0; i < UartTxBuff.Len; i++)   
	// 	{
	// 		if((UartTxBuff.Buf[i] == EOT) || (UartTxBuff.Buf[i] == SOH) 
	// 			|| (UartTxBuff.Buf[i] == DLE))
	// 		{
	// 			// EOT/SOH/DLE repeated in the data field, insert DLE.
	// 			Buff[BuffLen++] = DLE;		
	// 		}
	// 		Buff[BuffLen++] = UartTxBuff.Buf[i]; 
	// 	} 
	// 	// Mark end of frame with EOT.
	// 	Buff[BuffLen++] = EOT;  
	// 	UartTxBuff.Len = 0; 
	// }
	// return(BuffLen); 
    return 0;
}	


uint32_t  Last_Value=0;
uint32_t  Last_Addr=0;
uint32_t  Last_Flay=0;
/********************************************************************
* Function: 	UINT WriteHexRecord2Flash(uint8_t* HexRecord, UINT totalHexRecLen)
* Precondition: 
* Input: 		HexRecord buffer.
* Output:		None.
* Return:	    UINT   0  -- OK
*                      1  -- Write Flash Error
*                      2  -- Checksum mismatch 
*                      3  -- write and read mismatch
* Overview:     Writes hex record to flash.
*			
* Note:		 	None.
********************************************************************/	
UINT WriteHexRecord2Flash(uint8_t* HexRecord, UINT totalHexRecLen) 
{
	static T_HEX_RECORD HexRecordSt; 
	uint8_t Checksum = 0;
	UINT i;
	uint32_t WrData;
    uint32_t CheckData[4];
	uint32_t ProgAddress;
	UINT Result;
	uint32_t nextRecStartPt = 0;
  
    Result = 0;   
    CRC_Data = Crc_Fun(HexRecord,totalHexRecLen,CRC_Data);
    
	while(totalHexRecLen>=5) // A hex record must be at least 5 bytes. (1 Data Len byte + 1 rec type byte+ 2 address bytes + 1 checksum)  
	{
		HexRecord = &HexRecord[nextRecStartPt];  
		HexRecordSt.RecDataLen = HexRecord[0];  
		HexRecordSt.RecType = HexRecord[3];	 
		HexRecordSt.Data = &HexRecord[4];  
		
		//Determine next record starting point.
		nextRecStartPt = HexRecordSt.RecDataLen + 5;	
		
		// Decrement total hex record length by length of current record.
		totalHexRecLen = totalHexRecLen - nextRecStartPt;  
		
		// Hex Record checksum check.
		Checksum = 0;
		for(i = 0; i < HexRecordSt.RecDataLen + 5; i++)
		{
			Checksum += HexRecord[i];   
		}	
		
	    if(Checksum != 0)
	    {
		    //Error. Hex record Checksum mismatch.
            Result = 0x0002;    // Checksum mismatch
		} 
		else 
		{
			// Hex record checksum OK.
			switch(HexRecordSt.RecType) 
			{
				case DATA_RECORD:  //Record Type 00, data record. 
                    HexRecordSt.Address.byte.MB = 0;
					HexRecordSt.Address.byte.UB = 0;
					HexRecordSt.Address.byte.HB = HexRecord[1]; 
					HexRecordSt.Address.byte.LB = HexRecord[2];
                    
                    // Derive the address.
					HexRecordSt.Address.Val = HexRecordSt.Address.Val + HexRecordSt.ExtLinAddress.Val + HexRecordSt.ExtSegAddress.Val;
                    
                    while(HexRecordSt.RecDataLen)
                    {
                        ProgAddress = (uint32_t)(PA_TO_KVA0(HexRecordSt.Address.Val));
                        if((ProgAddress >= USER_ERASE_ADDRESS) && (ProgAddress < USER_MSG_ADDRESS))
                        {
                             if(HexRecordSt.RecDataLen < 4)
                                {
                                    // Sometimes record data length will not be in multiples of 4. Appending 0xFF will make sure that..
                                    // we don't write junk data in such cases.
                                    WrData = 0xFFFFFFFF;
                                    memcpy(&WrData, HexRecordSt.Data, HexRecordSt.RecDataLen);
                                }
                                else
                                {
                                    memcpy(&WrData, HexRecordSt.Data, 4);
                                }
                             
                                NVM_WordWrite(WrData,ProgAddress);
                                
                                // Increment the address.
                                HexRecordSt.Address.Val += 4;
                                // Increment the data pointer.
                                HexRecordSt.Data += 4;
                                // Decrement data len.
                                if(HexRecordSt.RecDataLen > 3)
                                {
                                    HexRecordSt.RecDataLen -= 4;
                                }
                                else
                                {
                                    HexRecordSt.RecDataLen = 0;
                                }
                            //while (!PLIB_NVM_FlashWriteCycleHasCompleted(NVM_ID_0));
                            while(NVMCONbits.WR);
                            //if (PLIB_NVM_WriteOperationHasTerminated(NVM_ID_0))
                            if(NVMCONbits.WRERR)
                            {                               
//                                APP_NVMClearError();
//                                return HEX_REC_PGM_ERROR;
                            }
                            if(NVM_Read((uint32_t*)CheckData,4,ProgAddress))
                            {
                                if(CheckData[0] != WrData)
                                {
                                    Result = 0x0003;
                                }
                            }
                        }
                    }
                    break;
				case EXT_SEG_ADRS_RECORD:  // Record Type 02    
				    HexRecordSt.ExtSegAddress.byte.MB = 0;
					HexRecordSt.ExtSegAddress.byte.UB = HexRecordSt.Data[0];
					HexRecordSt.ExtSegAddress.byte.HB = HexRecordSt.Data[1];
					HexRecordSt.ExtSegAddress.byte.LB = 0;
					// Reset linear address.
					HexRecordSt.ExtLinAddress.Val = 0;   
					break;					
				case EXT_LIN_ADRS_RECORD:   // Record Type 04    
					HexRecordSt.ExtLinAddress.byte.MB = HexRecordSt.Data[0];
					HexRecordSt.ExtLinAddress.byte.UB = HexRecordSt.Data[1];
					HexRecordSt.ExtLinAddress.byte.HB = 0;
					HexRecordSt.ExtLinAddress.byte.LB = 0;
					// Reset segment address.
					HexRecordSt.ExtSegAddress.Val = 0;   
					break;
					
				case END_OF_FILE_RECORD:  //Record Type 01, defines the end of file record.
				default: 
					HexRecordSt.ExtSegAddress.Val = 0;   
					HexRecordSt.ExtLinAddress.Val = 0;
					break;
			}		
		}	
	}
	return Result;	
}	



BOOL ExitFirmwareUpgradeMode(void)
{
	return RunApplication;  //static BOOL RunApplication = FALSE;  
}	



/********************************************************************
 * Static table used for the table_driven implementation.
 ********************************************************************/
static const uint16_t crc_table[16] = 
{
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef
};

/********************************************************************
* Function: 	CalculateCrc()
* Precondition: 
* Input: 		Data pointer and data length
* Output:		CRC.
* Side Effects:	None.
* Overview:     Calculates CRC for the given data and len		
* Note:		 	None.
********************************************************************/	
uint16_t Crc_Fun(uint8_t *data, uint32_t len, uint16_t init_crc)       
{
    UINT i;
    uint16_t crc = init_crc;
    
    while(len--)
    {
        i = (crc >> 12) ^ (*data >> 4);
	    crc = crc_table[i & 0x0F] ^ (crc << 4);
	    i = (crc >> 12) ^ (*data >> 0);
	    crc = crc_table[i & 0x0F] ^ (crc << 4);
	    data++;
	} 
    return (crc & 0xFFFF);
}


/********************************************************************
* Function: 	GetChar()
*
* Precondition: 
*
* Input: 		None
*
* Output:		True: If there is some data.
*
* Side Effects:	None.
*
* Overview:     Gets the data from UART RX FIFO.
*
*			
* Note:		 	None.
********************************************************************/
BOOL GetChar(uint8_t *byte)
{
    //rfid
//    if(U1STAHbits.URXBE==0)
//    {
//        *byte = (uint8_t)U1RXREG;	
//        if ((U1STAbits.OERR == 1))
//        {
//            U1STAbits.OERR = 0;
//        }
//        return TRUE;
//    }
	return FALSE;
}

/********************************************************************
* Function: 	PutChar()
*
* Precondition: 
*
* Input: 		None
*
* Output:		None
*
* Side Effects:	None.
*
* Overview:     Puts the data into UART tx reg for transmission.
*
*			
* Note:		 	None.
********************************************************************/
void PutChar(uint8_t txChar)
{
    //rfid
//   while(U1STAHbits.UTXBF == 1);
//   U1TXREG = txChar;   
}


/* The CRC value of the high byte */
const uint8_t auchCRC_Hi[] = {
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0,
	0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
	0x81, 0x40, 0x01,
	0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
	0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
	0x41, 0x01, 0xC0,
	0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
	0x80, 0x41, 0x01,
	0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00,
	0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81,
	0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0,
	0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
	0x81, 0x40, 0x01,
	0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01,
	0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
	0x40, 0x01, 0xC0,
	0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
	0x80, 0x41, 0x01,
	0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
	0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81,
	0x40
} ;

/* CRC value for the lowest byte  */
const uint8_t auchCRC_Lo[] = {
	0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
	0x05, 0xC5, 0xC4,
	0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB,
	0x0B, 0xC9, 0x09,
	0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE,
	0xDF, 0x1F, 0xDD,
	0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2,
	0x12, 0x13, 0xD3,
	0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
	0x36, 0xF6, 0xF7,
	0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E,
	0xFE, 0xFA, 0x3A,
	0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B,
	0x2A, 0xEA, 0xEE,
	0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27,
	0xE7, 0xE6, 0x26,
	0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
	0x63, 0xA3, 0xA2,
	0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD,
	0x6D, 0xAF, 0x6F,
	0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8,
	0xB9, 0x79, 0xBB,
	0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4,
	0x74, 0x75, 0xB5,
	0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
	0x50, 0x90, 0x91,
	0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94,
	0x54, 0x9C, 0x5C,
	0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59,
	0x58, 0x98, 0x88,
	0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D,
	0x4D, 0x4C, 0x8C,
	0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
	0x41, 0x81, 0x80,
	0x40
};

uint16_t CRC16_Chk(uint8_t *puchMsg, uint8_t usDataLen)
{
	uint8_t uchCRCHi = 0xFF;  /* High byte initialization for CRC  */
	uint8_t uchCRCLo = 0xFF;  /* Low-byte initialization of CRC  */
	uint8_t uIndex ;          /* CRC queries the table index */
	uint16_t u16Result;
	while(usDataLen--)        /* Complete the entire packet buffer */
	{
		uIndex = uchCRCLo ^ *puchMsg++ ;  /* Calculating CRC */
		uchCRCLo = uchCRCHi ^ auchCRC_Hi[uIndex];
		uchCRCHi = auchCRC_Lo[uIndex];
	}
	u16Result = uchCRCHi;
	u16Result <<= 8;
	u16Result += uchCRCLo;
	return u16Result;
}

